import path from 'path';
import fs from 'fs';
import type { Resolver } from 'webpack';
import type { Configuration } from "webpack";
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import TerserPlugin from 'terser-webpack-plugin';
import { findPagesAndEntries, createStyleHandler } from './utils.js';
import webpack from 'webpack';
import { fileURLToPath } from 'url';
import { info } from 'console';

/**
 * 将当前模块的 URL 转换为文件路径
 */
const __filename = fileURLToPath(import.meta.url);
/**
 * 获取当前模块所在的目录路径
 */
const __dirname = path.dirname(__filename);

/**
 * 项目根目录路径
 */
const ROOT_DIR = path.resolve(__dirname, '../..');
/**
 * 项目源代码目录路径
 */
const WWW_DIR = path.resolve(ROOT_DIR, "www");
/**
 * 构建输出目录路径
 */
const DIST_DIR = path.resolve(ROOT_DIR, 'dist');
/**
 * 构建配置相关代码目录路径
 */
const BUILDER_DIR = path.resolve(ROOT_DIR, 'builder');
/**
 * 资源目录路径
 */
const RES_DIR = path.resolve(ROOT_DIR, 'res');

/**
 * 创建 Webpack 配置的工厂函数
 * 根据传入的是否为开发模式的标志，生成对应的 Webpack 配置对象
 * 
 * @param {boolean} isDev - 指示是否为开发模式
 * @returns {Configuration} 生成的 Webpack 配置对象
 */
export function createConfig(isDev: boolean): Configuration {
    // 查找所有页面和入口
    const { pages, entries } = findPagesAndEntries(WWW_DIR);
    // 创建样式处理器
    const style = createStyleHandler();

    return {
        /**
         * 指定 Webpack 打包的目标环境，设置为 'web' 表示打包后的代码在浏览器环境运行
         */
        target: 'web',

        /**
         * 设置 Webpack 解析入口文件和加载器的基础目录，指定为项目源代码目录
         */
        context: WWW_DIR,

        /**
         * 配置 Webpack 打包的入口文件，使用 findPagesAndEntries 函数生成的入口对象
         */
        entry: entries,

        /**
         * 配置打包输出相关信息
         */
        output: {
            /**
             * 指定打包输出文件的目录
             */
            path: DIST_DIR,
            /**
             * 指定入口文件打包后的文件名，开发模式用简单文件名，生产模式添加内容哈希用于缓存控制
             */
            filename: isDev ? '[name].js' : '[name].[contenthash:8].js',
            /**
             * 指定非入口文件（如代码分割产生的 chunk）打包后的文件名，区分开发和生产模式
             */
            chunkFilename: isDev ? '[name].chunk.js' : '[name].[contenthash:8].chunk.js',
            /**
             * 指定资源模块打包后的文件名规则
             */
            assetModuleFilename: 'assets/[name].[hash:8][ext]',
            /**
             * 指定打包后资源的公共路径，设置为根路径
             */
            publicPath: '/',
            /**
             * 配置打包时清理输出目录的行为，通过 keep 函数保留特定文件
             */
            clean: {
                keep: (filename: string) => {
                    // 确保跨平台路径匹配
                    const normalizedPath = path.normalize(filename);
                    // 保留 /dist/common/miniui 和PHP目录及其子目录下的文件
                    const rtn = normalizedPath.includes(path.join('common', 'miniui') + path.sep) ||
                        normalizedPath.endsWith(path.join('common', 'miniui')) ||
                        normalizedPath.includes("php" + path.sep) ||
                        normalizedPath.endsWith(path.join('php'));
                    return rtn;
                }
            },
            /**
             * 开发模式下在打包文件中包含模块信息，方便调试
             */
            pathinfo: isDev,
            /**
             * 指定打包后代码支持的 JavaScript 特性
             */
            environment: {
                arrowFunction: true,
                bigIntLiteral: false,
                const: true,
                destructuring: true,
                dynamicImport: true,
                forOf: true,
                module: true,
                optionalChaining: true,
                templateLiteral: true
            }
        },

        /**
         * 配置 Webpack 的优化策略
         */
        optimization: {
            /**
             * 配置模块 ID 的生成方式，开发模式用可读名称，生产模式用确定性 ID 优化缓存
             */
            moduleIds: isDev ? 'named' : 'deterministic',
            /**
             * 配置 chunk ID 的生成方式，区分开发和生产模式
             */
            chunkIds: isDev ? 'named' : 'deterministic',
            /**
             * 配置代码分割策略
             */
            splitChunks: {
                /**
                 * 指定哪些 chunk 参与代码分割，'all' 表示所有 chunk 都参与
                 */
                chunks: 'all',
                /**
                 * 指定分割后的 chunk 最小大小，单位为字节
                 */
                minSize: 20000,
                /**
                 * 指定分割后剩余 chunk 的最小大小，单位为字节
                 */
                minRemainingSize: 0,
                /**
                 * 指定模块被引用的最小次数，达到该次数才会被分割
                 */
                minChunks: 1,
                /**
                 * 指定异步请求的最大并行数
                 */
                maxAsyncRequests: 30,
                /**
                 * 指定初始请求的最大并行数
                 */
                maxInitialRequests: 30,
                /**
                 * 强制分割的大小阈值，单位为字节
                 */
                enforceSizeThreshold: 50000,
                /**
                 * 配置缓存组，将符合条件的模块提取到单独的 chunk 中
                 */
                cacheGroups: {
                    /**
                     * 提取包含 'jquery' 的模块到单独的 'jquery' chunk 中
                     */
                    jquery: {
                        /**
                         * 正则表达式，匹配包含 'jquery' 的模块
                         */
                        test: /jquery/i,
                        /**
                         * 指定提取后的 chunk 名称
                         */
                        name: 'jquery',
                        /**
                         * 指定提取后的 chunk 输出文件名规则
                         */
                        filename: 'common/[name].js',
                        /**
                         * 指定缓存组的优先级，值越大优先级越高
                         */
                        priority: 0,
                        /**
                         * 如果已经有相同的 chunk 存在，则复用该 chunk
                         */
                        reuseExistingChunk: true
                    },
                    /**
                     * 提取包含 'miniui' 的模块到单独的 'miniui' chunk 中
                     */
                    miniui: {
                        test: /miniui/i,
                        name: 'miniui',
                        filename: 'common/[name].js',
                        priority: -5,
                        reuseExistingChunk: true,
                        chunks: 'all',
                    },
                    defaultVendors: {
                        filename: 'common/[chunkhash].js',
                        minChunks: 2,
                        priority: -10,
                        reuseExistingChunk: true,
                        chunks: 'all',
                    },
                    /**
                     * 默认缓存组，提取被 2 个及以上 chunk 引用的模块
                     */
                    default: {
                        filename: 'common/[chunkhash].js',
                        minChunks: 2,
                        priority: -20,
                        reuseExistingChunk: true,
                        chunks: 'all',
                    }
                },
            },
            /**
             * 配置运行时代码的提取方式，将运行时代码提取到 'common/runtime' 文件中
             */
            runtimeChunk: {
                name: (entrypoint: any) => `common/runtime`
            },

            /**
             * 配置压缩工具
             */
            minimizer: isDev ? [] : [
                // 使用 TerserPlugin 压缩 JavaScript 代码
                new TerserPlugin({
                    terserOptions: {
                        // 最大化压缩选项
                          compress: {
                              // 删除console语句
                              drop_console: true,
                              // 删除debugger语句
                              drop_debugger: true,
                              // 执行多次压缩以获得最佳效果
                              passes: 4,
                              // 移除未使用的变量和函数
                              unused: true,
                              // 删除死代码
                              dead_code: true,
                              // 内联常量表达式
                              evaluate: true,
                              // 折叠布尔表达式
                              booleans: true,
                              // 简化循环
                              loops: true,
                              // 合并变量声明
                              collapse_vars: true,
                              // 内联单用途变量
                              inline: true,
                              // 优化条件表达式
                              conditionals: true,
                              // 合并连续的变量声明
                              join_vars: true,
                              // 优化属性访问
                              properties: true,
                              // 提升变量
                              hoist_vars: true,
                              // 提升函数声明
                              hoist_funs: true,
                              // 移除纯函数调用
                              pure_getters: true,
                              // 移除不必要的括号
                              reduce_vars: true,
                              // 优化switch语句
                              switches: true
                          },
                        // 严格的格式化选项 - 确保代码最小化
                        format: {
                            // 禁用美化 - 最关键的选项
                            beautify: false,
                            // 删除所有注释
                            comments: false,
                            // 启用分号以提高代码兼容性
                            semicolons: true,
                            // 控制括号格式
                            braces: false,
                            // 禁用缩进
                            indent_level: 0,
                            indent_start: 0,
                            // 强制为所有属性名添加引号以提高代码一致性
                            quote_keys: true,
                            // 禁用保留引号属性
                            keep_quoted_props: false,
                            // 确保IIFE格式紧凑
                            wrap_iife: true,
                            // 控制代码紧凑度
                            ecma: 2018
                        },
                        // 增强的混淆选项 - 不使用属性名混淆
                        mangle: {
                            // 混淆顶层变量
                            toplevel: true,
                            // 不保留函数名
                            keep_fnames: false,
                            // 禁用safari10兼容以获得更好的混淆效果
                            safari10: false,
                            // 使用最多3个字符的短变量名
                            eval: true
                        },
                        // 禁用源映射，防止代码泄露
                        sourceMap: false,
                        // 启用保留顶级别作用域
                        keep_classnames: false,
                        keep_fnames: false
                    },
                    // 使用多线程并行运行以提高构建速度
                    parallel: true,
                })
            ],
            /**
             * 移除空的 chunk
             */
            removeEmptyChunks: true,
            /**
             * 合并重复的 chunk
             */
            mergeDuplicateChunks: true,
            /**
             * 移除已经存在的模块
             */
            removeAvailableModules: true,
        },

        /**
         * 优化 Webpack 的文件监听配置，忽略指定目录下的文件变化
         */
        watchOptions: {
            /**
             * 指定需要忽略的文件或目录，排除部分目录以减少文件系统的访问次数
             */
            ignored: [
                `!${WWW_DIR}/**/*`,
                `${WWW_DIR}/node_modules/!(miniui-components)/**/*`,
                '**/dist/**',
                '**/res/**',
                '**/.git/**'
            ],
            /**
             * 指定文件变化后触发重新构建的聚合时间，单位为毫秒
             */
            aggregateTimeout: 500,
            /**
             * 指定文件监听的轮询间隔，单位为毫秒
             */
            poll: 1000
        },

        /**
         * 配置 Webpack 解析模块的规则
         */
        resolve: {
            /**
             * 指定文件扩展名的解析顺序，当引入模块时省略扩展名，Webpack 按此顺序查找文件
             */
            extensions: [
                '.mts', '.cts',
                '.ts', '.tsx',
                '.mjs', '.js',
                '.jsx', '.json'
            ],
            /**
             * 指定 Webpack 查找模块的目录
             */
            modules: [
                WWW_DIR,
                path.resolve(WWW_DIR, 'node_modules'),
            ],
            /**
             * 配置模块别名，方便在代码中使用简短的路径引用模块
             */
            alias: {
                '@': WWW_DIR,
                '@pages': path.resolve(WWW_DIR, 'pages'),
                '@res': RES_DIR,
            },
            /**
             * 配置模块解析的回退策略，禁用部分 Node.js 内置模块
             */
            fallback: {
                crypto: false,
                buffer: false,
                path: false
            },
            /**
             * 配置解析插件，添加自定义插件处理以 '/res' 开头的路径
             */
            plugins: [
                {
                    apply: (resolver: Resolver) => {
                        // 处理以 /res 开头的路径
                        resolver.hooks.resolve.tapAsync('ResPathResolver', (request: any, resolveContext: any, callback: any) => {
                            if (request.request?.startsWith('/res/')) {
                                // 将绝对路径转换为相对于项目根目录的路径
                                request.request = path.join(ROOT_DIR, request.request.slice(1));
                            }
                            callback();
                        });
                    }
                }
            ],
        },

        /**
         * 配置 Webpack 解析加载器的规则
         */
        resolveLoader: {
            /**
             * 指定 Webpack 查找加载器的目录，优先在 builder 目录下的 node_modules 中查找
             */
            modules: [
                path.resolve(BUILDER_DIR, 'node_modules'),
            ]
        },

        /**
         * 配置 Webpack 处理不同类型模块的规则
         */
        module: {
            rules: [
                /**
                 * 处理 TypeScript 文件的规则
                 */
                {
                    /**
                     * 正则表达式，匹配以 .ts 或 .tsx 结尾的文件
                     */
                    test: /\.tsx?$/,
                    /**
                     * 使用 ts-loader 处理 TypeScript 文件
                     */
                    loader: 'ts-loader',
                    /**
                     * 配置 ts-loader 的选项
                     */
                    options: {
                        /**
                         * 指定 TypeScript 配置文件的路径
                         */
                        configFile: path.resolve(WWW_DIR, 'tsconfig.json'),
                        /**
                         * 开发模式下启用快速编译，仅进行转译不进行类型检查，提高编译速度
                         */
                        transpileOnly: isDev,
                        /**
                         * 启用实验性的文件监听 API，提升构建性能
                         */
                        experimentalWatchApi: true,
                        /**
                         * 根据开发和生产模式配置 TypeScript 编译器选项，生产模式下启用代码输出
                         */
                        compilerOptions: isDev ? {} : { noEmit: false },
                    },
                    /**
                     * 排除 node_modules 目录下的文件，避免对第三方库进行处理
                     */
                    exclude: /node_modules\/(?!miniui-components)/
                },

                /**
                 * 处理普通 CSS 文件的规则，将 CSS 写入到 HTML 文件的 head 中
                 */
                {
                    test: /.css$/,
                    exclude: [
                        /mini.*?\.css$/i,
                        /min\.css$/i,
                    ],
                    /**
                     * 使用 style-loader 和 css-loader 处理 CSS 文件
                     */
                    use: ['style-loader', 'css-loader']
                },

                /**
                 * 处理 miniui 相关 CSS 文件的规则，将 CSS 提取到单独的文件中
                 */
                {
                    test: /mini.*?\.css$/i,
                    use: [MiniCssExtractPlugin.loader, 'css-loader'],
                },

                /**
                 * 处理 miniui 引入的 JS 文件的规则
                 */
                {
                    test: /miniui\.js/i,
                    use: [
                        {
                            /**
                             * 使用 imports-loader 为模块注入依赖，注入 jQuery
                             */
                            loader: 'imports-loader',
                            options: {
                                /**
                                 * 指定模块的运行环境为 window
                                 */
                                wrapper: "window",
                                /**
                                 * 为模块添加额外的代码，引入 jQuery
                                 */
                                additionalCode: `window.jQuery = require('jquery');`,
                            }
                        },
                        {
                            /**
                             * 使用 exports-loader 将模块导出为 CommonJS 模块
                             */
                            loader: "exports-loader",
                            options: {
                                /**
                                 * 指定导出模块的类型为 CommonJS
                                 */
                                type: "commonjs",
                                /**
                                 * 指定导出的模块名称为 'mini'
                                 */
                                exports: 'mini'
                            }
                        }
                    ]
                },

                /**
                 * 处理 JSON 和 TXT 文件的规则
                 * 将这些文件作为资源文件处理，并复制到 /assets/data 目录下
                 */
                {
                    test: /\.(json|txt)$/i,
                    type: 'asset/resource',
                    generator: {
                        filename: 'assets/data/[name].[hash:8][ext]'
                    }
                },

                /**
                 * 处理常见图片格式的规则，使用 Webpack 的 asset 模块自动选择资源类型
                 */
                {
                    test: /\.(png|jpe?g|gif|svg|webp)$/i,
                    oneOf: [
                        // 处理以 no-inline 结尾的图片资源，不内联到代码中，输出到指定目录
                        {
                            resourceQuery: /no-inline/,
                            type: 'asset/resource',
                            generator: {
                                filename: 'assets/images/[name].[hash:8][ext]'
                            }

                        },
                        // 处理其他图片资源，自动选择内联或输出到文件
                        {
                            /**
                            * 使用 asset 模块处理图片资源，自动选择内联或输出到文件
                            */
                            type: 'asset',
                            /**
                             * 配置解析器选项，设置 base64 内联的阈值
                             */
                            parser: {
                                dataUrlCondition: {
                                    /**
                                     * 8KB 以下的图片将被转换为 base64 内联到代码中
                                     */
                                    maxSize: 8 * 1024
                                }
                            },
                            /**
                             * 配置生成器选项，指定大于阈值的图片输出路径规则
                             */
                            generator: {
                                /**
                                 * 大于阈值的图片将输出到指定目录，并添加哈希值用于缓存控制
                                 */
                                filename: 'assets/images/[name].[hash:8][ext]'
                            }
                        }
                    ]

                },

                /**
                 * 处理 HTML 文件的规则，使用自定义的 html-loader-withhook 加载器
                 */
                {
                    test: /\.html$/i,
                    loader: 'html-loader-withhook',
                    options: {
                        /**
                         * 配置 HTML 加载器的资源处理选项
                         */
                        sources: {
                            attributes: {
                                style,
                                url: (param: any, sources: any) => {
                                    const match = param.value.match(/url=["']([^"']+\.(?:png|jpe?g|gif|svg|webp|json)).*?["']/i);
                                    if (match && match[1]) {
                                        const [fullMatch, url] = match;
                                        const offset = fullMatch.indexOf(url!) + param.startOffset;
                                        sources.push({
                                            name: 'url',
                                            value: url,
                                            isValueQuoted: false,
                                            startOffset: offset,
                                            endOffset: offset + url!.length
                                        });
                                        return true;
                                    }
                                    return false;
                                },
                                img: (param: any, sources: any) => {
                                    const match = param.value.match(/img=["']([^"']+\.(?:png|jpe?g|gif|svg|webp|json)).*?["']/i);
                                    if (match && match[1]) {
                                        const [fullMatch, url] = match;
                                        const offset = fullMatch.indexOf(url!) + param.startOffset;
                                        sources.push({
                                            name: 'url',
                                            value: url,
                                            isValueQuoted: false,
                                            startOffset: offset,
                                            endOffset: offset + url!.length
                                        });
                                        return true;
                                    }
                                    return false;
                                }
                            },
                            list: ["..."]
                        }
                    }
                }
            ]
        },

        /**
         * 配置开发工具，开发模式下生成便宜的源映射文件，方便调试
         */
        devtool: isDev ? 'cheap-module-source-map' : false,

        /**
         * 指定 Webpack 的打包模式，开发模式或生产模式
         */
        mode: isDev ? 'development' : 'production',

        /**
         * 配置 Webpack 的性能提示，生产模式下对资源大小进行检查并给出警告
         */
        performance: isDev ? false : {
            /**
             * 当资源大小超过限制时给出警告提示
             */
            hints: 'warning',
            /**
             * 指定单个资源文件的最大大小，单位为字节
             */
            maxAssetSize: 250 * 1024,
            /**
             * 指定入口文件的最大大小，单位为字节
             */
            maxEntrypointSize: 250 * 1024,
            /**
             * 配置资源过滤函数，对 miniui 资源放宽限制
             */
            assetFilter: (assetFilename: string, asset: any, assetInfo: any) => {
                // 对 miniui 资源放宽限制（2MB）
                if (/miniui/.test(assetFilename)) {
                    if (assetInfo.size > 1 * 1024 * 1024) {
                        console.warn(`Asset ${assetFilename} exceeds the 2MB limit.`);
                    }
                    return false;
                }
                return true;
            }
        },

        /**
         * 配置 Webpack 打包过程中的统计信息输出
         */
        stats: {
            /**
             * 启用彩色输出，使日志更易读
             */
            colors: true,
            /**
             * 不输出模块信息
             */
            modules: false,
            /**
             * 不输出子模块信息
             */
            children: false,
            /**
             * 不输出 chunk 信息
             */
            chunks: false,
            /**
             * 不输出 chunk 模块信息
             */
            chunkModules: false,
            /**
             * 输出错误信息
             */
            errors: true,
            /**
             * 输出警告信息
             */
            warnings: true,
            /**
             * 输出资源信息
             */
            assets: true,
            /**
             * 输出 Webpack 版本信息
             */
            version: true,
            /**
             * 输出打包结果的哈希值
             */
            hash: true,
            /**
             * 输出打包耗时信息
             */
            timings: true,
            /**
             * 输出打包完成时间信息
             */
            builtAt: true
        },

        /**
         * 配置 Webpack 的缓存策略，使用文件系统缓存以提高构建性能
         */
        cache: {
            /**
             * 指定缓存类型为文件系统缓存
             */
            type: 'filesystem',
            /**
             * 配置缓存的依赖项，当配置文件发生变化时，缓存将失效
             */
            buildDependencies: {
                config: [__filename]
            },
            /**
             * 根据开发和生产模式设置不同的缓存名称
             */
            name: isDev ? 'development-cache' : 'production-cache'
        },

        /**
         * 配置 Webpack 的实验性特性，启用顶级 await 和异步 WebAssembly 支持
         */
        experiments: {
            topLevelAwait: true,
            asyncWebAssembly: true
        },

        /**
         * 配置 Webpack 的插件，使用 HtmlWebpackPlugin 和 MiniCssExtractPlugin
         */
        plugins: [
            /**
             * 为每个页面创建一个 HtmlWebpackPlugin 实例，生成 HTML 文件并注入打包后的脚本和样式
             */
            ...pages.map(page => new HtmlWebpackPlugin({
                /**
                 * 指定 HTML 模板文件的路径
                 */
                template: page.template,
                /**
                 * 指定生成的 HTML 文件的文件名
                 */
                filename: page.filename,
                /**
                 * 指定需要注入到 HTML 文件中的 chunk 名称
                 */
                chunks: page.template.includes("login") ? [page.chunk] : [page.chunk, "login"],
                /**
                 * 自动将脚本和样式注入到 HTML 文件中
                 */
                inject: true,
                /**
                 * 指定脚本的加载方式为阻塞式加载
                 */
                scriptLoading: "blocking",
                /**
                 * 为首页添加 favicon
                 */
                favicon: page.filename == "index.html" ? path.join(ROOT_DIR, "/res/icon/favicon.ico") : false,
                /**
                 * 生产模式下对 HTML 文件进行压缩
                 */
                minify: !isDev ? {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeStyleLinkTypeAttributes: true,
                    keepClosingSlash: true,
                    minifyJS: true,
                    minifyCSS: true,
                    minifyURLs: true,
                } : false
            })),
            /**
             * 使用 MiniCssExtractPlugin 将 CSS 提取到单独的文件中
             */
            new MiniCssExtractPlugin(),
            ...(isDev ? [new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify('development')
            })] : []),
        ]
    };
}

